﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Chisholm.Windows.Live.SSO;
using Chisholm.Windows.Live.Library;
using System.ServiceModel;
using System.Security.Principal;

namespace Chisholm.Windows.Live.SSOClient
{
    /// <summary>
    /// This is the main Windows Live SSO Client class for authenticating users to the Windows 
    /// Live Single Sign On Service, either directly through the Live@EDU credential server or
    /// through the middle tier SSO Server service
    /// </summary>
    public class SSOClient : ISSOServerEngine
    {
        #region Middle Tier SSO Server Settings

        private string _servername;
        /// <summary>
        /// The name of the SSO Server to connect to, if using the middle tier service
        /// </summary>
        public string ServerName
        {
            get { return _servername; }
            set { _servername = value; }
        }

        private int _port;
        /// <summary>
        /// The port that the SSO Server is exposed on, if using the middle tier service
        /// </summary>
        public int Port
        {
            get { return _port; }
            set { _port = value; }
        }

        private string _username;
        /// <summary>
        /// The full DOMAIN and USERNAME to use to access the SSO Server, if using the middle tier service
        /// </summary>
        public string Username
        {
            get { return _username; }
            set { _username = value; }
        }

        private string _password;
        /// <summary>
        /// The password for the DOMAIN/USER account being used to access the SSO Server, if using the middle tier service
        /// </summary>
        public string Password
        {
            get { return _password; }
            set { _password = value; }
        }

        /// <summary>
        /// The internal client connection, if using the middle tier service
        /// </summary>
        private SSOServerClient _client;

        #endregion

        #region Direct Microsoft Credential Server Settings

        private int _siteid;
        /// <summary>
        /// The Windows Live@EDU Site ID, if directly accessing the Microsoft Credential Server
        /// </summary>
        public int SiteID
        {
            get { return _siteid; }
            set { _siteid = value; }
        }

        private string _webproxy;
        /// <summary>
        /// The web proxy to access the internet, if required and directly accessing the Microsoft
        /// Credential Server
        /// </summary>
        public string WebProxy
        {
            get { return _webproxy; }
            set { _webproxy = value; }
        }

        private int _logintimeout;
        /// <summary>
        /// The time out (in seconds) before Short Live Token's will be expirted, if directly accessing
        /// the Microsoft Credential Server
        /// </summary>
        public int LoginTimeout
        {
            get { return _logintimeout; }
            set { _logintimeout = value; }
        }

        private string _certificatefile;
        /// <summary>
        /// The full path and location of the SSL certificate issued, if it is being accessed from the
        /// web server path directly and directly accessing the Microsoft Credential Server
        /// </summary>
        public string CertificateFile
        {
            get { return _certificatefile; }
            set { _certificatefile = value; }
        }

        private string _certificatepassword;
        /// <summary>
        /// The password for the SSL certificate issued, if it is being accessed from the web server
        /// path directly and directly accessing the Microsoft Credential Server
        /// </summary>
        public string CertificatePassword
        {
            get { return _certificatepassword; }
            set { _certificatepassword = value; }
        }

        private string _certificatethumbprint;
        /// <summary>
        /// The thumb print of the SSL Certificate issued and stored in the Personal store of the 
        /// Local Machine, if directly accessing the Microsoft Credential Server
        /// </summary>
        public string CertificateThumbprint
        {
            get { return _certificatethumbprint; }
            set { _certificatethumbprint = value; }
        }

        #endregion

        #region Constructors and Initialisation Functions

        /// <summary>
        /// Stores the type of access (whether direct to the Microsoft Credential Server or via 
        /// the middle tier Single Sign On Server) being used
        /// </summary>
        private SSOAuthenticationMethods _accesstype;

        /// <summary>
        /// This initialises the SSO client using the SSO Server Middle Tier to perform the Live@EDU
        /// SSO Authentication on behalf of the client
        /// </summary>
        public SSOClient(string servername, int port, string username, string password)
        {
            _servername = servername;
            _port = port;
            _username = username;
            _password = password;
            _accesstype = SSOAuthenticationMethods.SSOServer;
        }

        /// <summary>
        /// This initialises the SSO client using the direct access method to Microsoft's Credential Server,
        /// assuming the certificate file supplied for use with Live@EDU Single Sign On (SSO) is located
        /// on the local hard disk of the Web Server
        /// </summary>
        /// <param name="siteid">The Site ID supplied by Microsoft's Live@EDU Ed-Desk</param>
        /// <param name="webproxy">If a proxy server is in place and required to access the internet,
        /// this is the proxy server address</param>
        /// <param name="logintimeout">The timeout (in seconds) for issued Live@EDU Short Lived Token's
        /// to expire once authenticated</param>
        /// <param name="certificatefile">The full path and file name of the SSL Certificate issued by
        /// Live@EDU to access the Single Sign On Credential Service</param>
        /// <param name="certificatepassword">The password for the supplied certificate</param>
        public SSOClient(int siteid, string webproxy, int logintimeout, string certificatefile,
            string certificatepassword)
        {
            _siteid = siteid;
            _webproxy = webproxy;
            _logintimeout = logintimeout;
            _certificatefile = certificatefile;
            _certificatepassword = certificatepassword;
            _certificatethumbprint = String.Empty;
            _accesstype = SSOAuthenticationMethods.Direct;
        }

        /// <summary>
        /// This initialises the SSO client using the direct access method to Microsoft's Credential Server,
        /// assuming the certificate file supplied for use with Live@EDU Single Sign On (SSO) is located
        /// on the local hard disk of the Web Server
        /// </summary>
        /// <param name="siteid">The Site ID supplied by Microsoft's Live@EDU Ed-Desk</param>
        /// <param name="webproxy">If a proxy server is in place and required to access the internet,
        /// this is the proxy server address</param>
        /// <param name="logintimeout">The timeout (in seconds) for issued Live@EDU Short Lived Token's
        /// to expire once authenticated</param>
        /// <param name="certificatethumbprint">The thumb print of the SSL Certificate issued and stored
        /// in the Personal store of the Local Machine</param>
        public SSOClient(int siteid, string webproxy, int logintimeout, string certificatethumbprint)
        {
            _siteid = siteid;
            _webproxy = webproxy;
            _logintimeout = logintimeout;
            _certificatefile = String.Empty;
            _certificatepassword = String.Empty;
            _certificatethumbprint = certificatethumbprint;
            _accesstype = SSOAuthenticationMethods.Direct;
        }

        /// <summary>
        /// This opens the client connection to the SSO Server
        /// </summary>
        private void OpenClient()
        {
            // Prepare our binding and address
            string address = "net.tcp://" + _servername + ":" + _port.ToString()
                        + "/SSOServer";
            NetTcpBinding tcpbinding = new NetTcpBinding();
            tcpbinding.MaxReceivedMessageSize = Int32.MaxValue;
            tcpbinding.ReaderQuotas.MaxStringContentLength = Int32.MaxValue;
            tcpbinding.ReaderQuotas.MaxDepth = Int32.MaxValue;
            tcpbinding.ReceiveTimeout = new TimeSpan(0, 60, 0);
            tcpbinding.SendTimeout = new TimeSpan(0, 60, 0);
            tcpbinding.CloseTimeout = new TimeSpan(0, 60, 0);

            // Then initialise our client
            _client = new SSOServerClient(tcpbinding, new EndpointAddress(address));

            // Then set our authentication
            string domain = String.Empty;
            string username = String.Empty;
            if (_username.Contains("\\"))
            {
                domain = _username.Split('\\')[0];
                username = _username.Split('\\')[1];
            }
            else
            {
                domain = Environment.MachineName;
                username = _username;
            }
            _client.ClientCredentials.Windows.ClientCredential.Domain = domain;
            _client.ClientCredentials.Windows.ClientCredential.UserName = username;
            _client.ClientCredentials.Windows.ClientCredential.Password = _password;
            _client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;

            // Finally open the client
            _client.Open();
        }

        /// <summary>
        /// This closes the client connection to the SSO Server
        /// </summary>
        private void CloseClient()
        {
            // Simply close the client connection
            _client.Close();
        }

        #endregion

        #region Live@EDU SSO Client Functions

        /// <summary>
        /// This method authenticates the given Windows Live ID against the source server and returns
        /// the Short Lived Token (or an empty string if the user couldn't be authenticated)
        /// </summary>
        /// <param name="windowsliveid">The windows live ID of the user to authenticate</param>
        /// <returns>Either the issued Short Lived Token, or an empty string if an error occurred</returns>
        public string GetSLT(string windowsliveid)
        {
            // Check the type of access method being used to authenticate the Single Sign On (SSO) 
            // clients
            if (_accesstype == SSOAuthenticationMethods.SSOServer)
            {
                // As the middle tier SSO Server is being used, we need to access it and run the function
                OpenClient();

                // Then execute the method
                string result = _client.GetSLT(windowsliveid);

                // Finally close the client
                CloseClient();

                // Then return the result
                return result;
            }
            else
            {
                // As we are directly accessing the Microsoft Credential Service, we need to 
                // Initialise the Windows Live SSO client connection directly and run the function. To
                // do this we need to determine where the certificate is stored and provide it
                LiveSLT liveticket = null;
                if (_certificatefile == String.Empty)
                {
                    // The certificate file has not been specified so we presume it is stored in the local
                    // certificate store
                    liveticket = new LiveSLT(_certificatethumbprint, _siteid.ToString(),
                        _webproxy, _logintimeout);
                }
                else
                {
                    // As the certificate file has been specified, we will presume we use that first
                    liveticket = new LiveSLT(_certificatefile, _certificatepassword,
                        _siteid.ToString(), _webproxy, _logintimeout);
                }

                // Now we return the result
                return liveticket.GetSLT(windowsliveid);
            }
        }

        /// <summary>
        /// This method authenticates the given Windows Live ID against the source server and then, 
        /// depending on the desired service chosen, returns the redirection URL to that service with
        /// the short lived token and Windows Live ID embedded
        /// </summary>
        /// <param name="windowsliveid">The Windows Live ID of the user to authenticate</param>
        /// <param name="desiredservice">The desired service to access</param>
        /// <returns>The pre-prepared URL ready for redirection</returns>
        public string GetRedirectionURL(string windowsliveid, LiveServiceTypes desiredservice)
        {
            // Check the type of access method being used to authenticate the Single Sign On (SSO) 
            // clients
            if (_accesstype == SSOAuthenticationMethods.SSOServer)
            {
                // As the middle tier SSO Server is being used, we need to access it and run the function
                OpenClient();

                // Then execute the method
                string result = _client.GetRedirectionURL(windowsliveid, desiredservice);

                // Finally close the client
                CloseClient();

                // Then return the result
                return result;
            }
            else
            {
                // As we are directly accessing the Microsoft Credential Service, we need to 
                // Initialise the Windows Live SSO client connection directly and run the function. To
                // do this we need to determine where the certificate is stored and provide it
                LiveSLT liveticket = null;
                if (_certificatefile == String.Empty)
                {
                    // The certificate file has not been specified so we presume it is stored in the local
                    // certificate store
                    liveticket = new LiveSLT(_certificatethumbprint, _siteid.ToString(),
                        _webproxy, _logintimeout);
                }
                else
                {
                    // As the certificate file has been specified, we will presume we use that first
                    liveticket = new LiveSLT(_certificatefile, _certificatepassword,
                        _siteid.ToString(), _webproxy, _logintimeout);
                }

                // Now we return the result, based on the type of service desired
                switch (desiredservice)
                {
                    case LiveServiceTypes.Mail:
                        return liveticket.GetEmailRedirectLocation(windowsliveid);
                    case LiveServiceTypes.SkyDrive:
                        return liveticket.GetSkyDriveRedirectLocation(windowsliveid);
                    case LiveServiceTypes.Calendar:
                        return liveticket.GetCalendarRedirectLocation(windowsliveid);
                    case LiveServiceTypes.Photos:
                        return liveticket.GetPhotoRedirectLocation(windowsliveid);
                }               

                // By default, if the specified link wasn't found, direct to mail
                return liveticket.GetEmailRedirectLocation(windowsliveid);
            }
        }

        #endregion
    }
}
